/**
 * 循环字节队列
 * Copyright (c) 2017 深圳市智辰科技有限公司
 * All rights reserved.
 * 张勇 41204@qq.com
 */

#include <string.h>
#include "utils.h"
#include "qchar.h"

#if (configSUPPORT_DYNAMIC_ALLOCATION == 1)

QChar* QChar_create(uint16_t size) {
	if(!size) { return NULL; }
	QChar* q = MALLOC(QChar);
	if(!q) { return NULL; }

	q->array = unios_malloc(size);
	if(!q->array) { unios_free(q); return NULL; }

	unios_critical_section_init(&q->css);
	q->size = size;
	q->head = q->tail = 0;
	q->full = false;
	q->timestamp = 0;

	return q;
}

void QChar_destroy(QChar *q) {
	if(!q) { return; }
	unios_critical_section_destroy(&q->css);
	if(q->array) { unios_free(q->array); }
	unios_free(q);
}

#endif


uint16_t QChar_count(QChar *q) {
	if(!q) { return 0; }
    if(q->tail > q->head) {
        return (q->tail - q->head);
    } else if(q->tail < q->head) {
        return ((q->tail + q->size) - q->head);
    } else if(q->full) {
		return q->size;
	} else {
		return 0;
	}
}

uint16_t QChar_clear(QChar *q) {
	if(!q) { return 0; }
    QChar_lock(q);
	uint16_t len = QChar_count(q);

    q->full = false;
    q->head = q->tail = 0;
    q->array[0] = '\0';
	q->timestamp = 0;

    QChar_unlock(q);
	return len;
}

bool QChar_put(QChar *q, uint8_t item) {
	if(!q) { return false; }
    QChar_lock(q);

    if(!q || q->full) {
    	QChar_unlock(q);
		return false;
	}

	// 填写元素
    q->array[q->tail] = item;

	// 移动尾部指针
    q->tail = (q->tail + 1) % q->size;
    q->full = (q->tail == q->head);
	q->timestamp = unios_ms_now();

    QChar_unlock(q);
    return true;
}

bool QChar_putn(QChar *q, uint8_t *buf, uint16_t size) {
    uint16_t count;
	if(!q || !buf || !size) { return false; }
    QChar_lock(q);

    if(QChar_free(q) < size) {
    	QChar_unlock(q);
		return false;
	}

	// 先在尾部空闲位置填写
	count = q->size - q->tail;
	count = min(count, size);
    memcpy(q->array + q->tail, buf, count);

	// 如果还有数据没写完，则绕到头部继续写
	if(size > count) {
    	memcpy(q->array, buf+count, (size - count));
	}

	// 移动尾部指针
    q->tail = (q->tail + size) % q->size;
    q->full = (q->tail == q->head);
	q->timestamp = unios_ms_now();

    QChar_unlock(q);
    return true;
}

bool QChar_pop(QChar *q, uint8_t *item) {
	if(!q || !item) { return false; }
    QChar_lock(q);

	if(QChar_count(q) < 1) {
    	QChar_unlock(q);
		return false;
	}

	// 如果 item == NULL 则只是简单删除数据
	if(item) {
    	*item = q->array[q->head];
	}

	// 移动头部指针
    q->head = (q->head + 1) % q->size;
    q->full = false;

    QChar_unlock(q);
    return true;
}

uint16_t QChar_popn(QChar *q, uint8_t *buf, uint16_t size) {
    uint16_t count;
	if(!q || !buf || !size) { return 0; }
    QChar_lock(q);

	// 如果 buf == NULL 则只是简单删除数据
	if(buf) {
		// 计算最多可取多少数据
		count = QChar_count(q);
		size = min(count, size);

		// 先从head后面取数据
		count = q->size - q->head;
		count = min(count, size);
		memcpy(buf, q->array + q->head, count);

		// 如果还有数据没取完，则绕到头部继续取
		if(size > count) {
			memcpy(buf+count, q->array, (size - count));
		}
	}

	// 移动头部指针
    q->head = (q->head + size) % q->size;
    q->full = false;

    QChar_unlock(q);
    return size;
}

bool QChar_peekAt(QChar *q, int idx, uint8_t *item) {
    uint16_t pos;
	if(!q || !item) { return false; }

	if(idx >= 0) {
		if(QChar_count(q) < 1+idx) { return false; }
		pos = (q->head + idx) % q->size;
	} else {
		idx = -idx;
		if(QChar_count(q) < idx) { return false; }
		pos = (q->tail + q->size - idx) % q->size;
	}

	// 填写元素
    *item = q->array[pos];
    return true;
}

uint16_t QChar_peekn(QChar *q, uint8_t *buf, uint16_t size) {
    uint16_t count;
	if(!q || !buf || !size) { return 0; }
    QChar_lock(q);

	// 计算最多可取多少数据
	count = QChar_count(q);
	size = min(count, size);

	// 先从head后面取数据
	count = q->size - q->head;
	count = min(count, size);
    memcpy(buf, q->array + q->head, count);

	// 如果还有数据没取完，则绕到头部继续取
	if(size > count) {
    	memcpy(buf+count, q->array, (size - count));
	}

    QChar_unlock(q);
    return true;
}

bool QChar_cutn(QChar *q, uint16_t size) {
	if(!q || !size) { return false; }
    QChar_lock(q);

    if(QChar_count(q) < size) {
    	QChar_unlock(q);
		return false;
	}

    q->tail = (q->tail + q->size - size) % q->size;
    q->full = false;

    QChar_unlock(q);
    return true;
}

unios_ms_t QChar_age(QChar *q) {
	if(!q || !q->timestamp || !QChar_count(q)) { return 0; }
	return unios_ms_elapsed(q->timestamp);
}
